Beginning Windows
Volume Number: 2
Issue Number: 7
Column Tag: ABC's of C
By Bob Gordon, Apropos Publication Services, Contributing Editor
Probably the most visible part of the Macintosh user interface is the window.
Since we can't build much of an application without showing something on the screen,
we will begin to examine how to use the Macintosh Window Manager. At the same time,
we'll take a look at the C preprocessor, the if-statement, and review the topics from
last month-the Event Manager, structures, and the case statement.
Preprocess Your Code
The C preprocessor provides a variety of useful services. We saw last month
how to use the #define to provide a shorthand name by which to refer to a structure.
These 'define' statements and other data structures can be stored in a seperate ".h" file
and included into our source code at compile time. We can create our own new ".h" file
to reduce some of C's more arcane symbols. We will call this file "abc.h".
/* abc.h
*
* Local definitions to improve readability
*
*/
#define True 1
#define False 0
#define Nil 0
#define and &&
#define or ||
#define not !
#define equals ==
#define notequal !=
extern char *PtoCstr(); /* from stdio.h */
extern char *CtoPstr();
The first three definitions provide some standard constants. C has no boolean
type, but it is a good idea to use labeled constants rather than numbers when doing
logic. It makes the code easier to follow. "Nil" is used with pointers. Assign a pointer
Nil when you don't want it to point anywhere. C guarantees that a pointer can never
point to zero, so this is a safe initialization value.
We then have replacements for the logical operation symbols. Admittedly, these
take longer to type, but they are much easier to read (they are e specially helpful if you
must show your code to someone who doesn't use C), and they are safer. A very popular
C bug is to leave one of the equal signs out of the equality operator (see top of next
column):
if (a = b)
code;
instead of
if (a == b)
code;
The first is a perfectly legal C if-statement: it assigns the value of b to a, then if
a is non-zero, the code is executed. The second executes the code only if the value of a
equals the value of b. The cleverness of this bug is that not only is it legal, but many
times it is what you want to do. Using "equals" instead of "==" makes it much less
likely to change the meaning of a line by a typo, and it makes it much easier to find.
The last two entries are the Pascal-to-C and C-to-Pascal string conversion
utilities. These are normally defined in the stdio.h file, but since we are not including
that file, we can put them here. Remember, C and Pascal strings are different, so if we
send a string to a Toolbox routine, it must be a Pascal string. These are the functions
that Mac C has. Other compilers may have similar functions (Aztec C calls these
ctop() and ptoc()) or they will do the conversion automatically. Check your
documentation and place the appropriate functions in abc.h so you can use them without
repeating the external declarations in every source file.
To use abc.h, just have it as one of the include files at the begining of a source
file. All the definitions will then be available. We may add other definitions later.
By the way, the May 1986 Byte has an article called "Easy C" that describes a
considerably expanded set of preproccessor definitions. The authors replace many of
the standard C terms with new ones in an effort to increase readability and reduce
errors.
Fig. 1 Program output, window highlited.
if-then-else
There are several if-statements in the sample program. The if-statement is C's
other branching construct. Its general form is:
if (expression)
statement;
if (expression)
statement;
else /* shows optional else clause */
statement;
If the experession evaluates to a non-zero value, the statement is executed. If
the expression evaluates to zero and an else is present, the statement following the else
is executed. If no else is present, execution continues after the if.
If-statements may be nested, but the relation of else to if may be ambigous:
if (expression)
if (another expression)
statement;
else
statement;
Does the else go with the first if or the second? The layout on the page says it
will go with the first, but the compiler will place it with the second as it is closer. Use
braces to clear up ambiguities:
if (expression)
if (another expression)
statement;
}
else
statement;
Fig. 2 Mouse click outside the window
Fig. 3. Cmd T changes title name
Structures, Functions, and Pointers
The only other C issue we need to deal with is how to get structures in and out of
functions. The original definition of C did not allow functions to receive structures as
parameters or return them (the new ANSI standard does allow this, check your
compiler). A function could, however, receive or return a pointer to a structure. In C
a pointer is simply an address, and you can get the address of a variable with the
address operator (the ampersand). In the example, theEvent is an EventRecord
structure to get the next event from the Event Manager; the pointer (or address) to the
EventRecord is specified as &theEvent:
GetNextEvent(everyEvent,&theEvent);
This passes the address of theEvent to GetNextEvent, by specifying it as
&theEvent. This works well with all C compilers, but it does not work in all cases with
Toolbox functions. The problem is that Pascal allows structures ( records) to be passed
as parameters, as well as by address. On the Mac, only structures of four or fewer
bytes are passed as parameters; longer structures are passed by address. There is one
structure of four bytes, the Point, which we saw last month. Mac C handles this
automatically. They define the Point as one of the argument types that can be passed to
Toolbox routines. If you are using Mac C, pass the address of the Point. Aztec C, on the
other hand, uses a special function, pass() to pass points, as shown below:
FindWindow(pass(er.where), & whichWindow); /*aztec */
Finally, since structures are often used with pointers, C has a special operator
to access a member of a structure given a pointer to the structure. If er is an
EventRecord and erp is a pointer to an Event Record, the what field is accessed by:
er.what /* the what member */
erp->what /* the what member */
(*erp).what /* the what member */
The last example shows the indirection operator (the asterisk). It yields the
value at the address contained in the variable. The structure pointer operator (->) is
much easier to read.
Putting a Window on the Screen
The example program this month puts a window on the screen, changes its title,
and responds to certain mouse commands. The program deals with only one window and
does not include the change size command as multiple windows and changing the size
involves accessing the Memory Manager. We'll add these features after we cover it.
The program consists of five routines:
main()
Does initialization and calls the main event loop routine. InitWindows() must be
done if you want to use any of the Window Manager routines. See what happens if you do
not InitCursor(). The dragbounds rectangle limits the range of DragWindow(): it
ensures the window does not fall off the screen. Note that I used the preprocessor to
define Screen. This was done simply to avoid typing QD->screenBits.bounds. If we find
we need to use QD->screenBits.bounds a lot, we can add it to abc.h.
do window()
The do window() routine creates a new window on the desktop. As such, it is
primarily a call to the toolbox trap NewWindow(), which returns a window pointer to
the newly created window structure.
There are several potential trouble spots in NewWindow(). First, the window
record ( windowRec) must be static. I made it a global. Notice the use of the string
conversion routines. See what happens if you don't convert the string back. The
parameter, (WindowPtr)-1, is the behind parameter. We wish to place our new